/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author        Notes
 * 2018-12-13     balanceTWK    add sdcard port file
 * 2019-08-23     WillianChan   add spi sdcard port code
 */

#include <rtthread.h>

#include "drv_gpio.h"
#include "drv_spi.h"
#include "touch.h"
#include "drv_tfttouch.h"
#include "drv_tftlcd.h"

#define DBG_TAG "drv.touch"
#include <drv_log.h>
#define DBG_LVL DBG_INFO

#ifdef PKG_USING_LITTLEVGL2RTT
void littlevgl2rtt_send_input_event(rt_int16_t x, rt_int16_t y, rt_uint8_t state);
#endif

static struct rt_spi_device*    spi_touch_device;   /**< SPI interface */
static struct rt_touch_device   _touch;             /**< touch device */
struct rt_touch_data            touch_data;
static PosTypeDef touch_adjust_data = {             //定义一阵数据用来保存校正因数
    'Y',
    -12,
    -16,
    0.06445,
    0.09132,
};
                         
TouchTypeDef raw_touch;                             //定义用来存储读取到的数据


#define TOUCH_AdjDelay500ms() rt_thread_mdelay(500)

#if 0
static uint8_t spi1_read_write_byte(uint8_t tx_data)
{
    return rt_spi_sendrecv8(spi_touch_device, tx_data);
}

static uint16_t touch_read_data(uint8_t cmd) // 这种方式时序对不上
{
    uint8_t i, j;
    uint16_t read_value[TOUCH_READ_TIMES], value;
    uint32_t total_value;
	
    /* 读取TOUCH_READ_TIMES次触摸值 */
    for (i = 0; i < TOUCH_READ_TIMES; i++)
    {   
        /* 在差分模式下，XPT2046转换需要24个时钟，8个时钟输入命令，之后1个时钟去除 */
        /* 忙信号，接着输出12位转换结果，剩下3个时钟是忽略位 */    
        spi1_read_write_byte(cmd); // 发送命令，选择X轴或者Y轴 
        
        /* 读取数据 */
        read_value[i] = spi1_read_write_byte(0xFF);
        read_value[i] <<= 8;
        read_value[i] |= spi1_read_write_byte(0xFF);
        
        /* 将数据处理，读取到的AD值的只有12位，最低三位无用 */
        read_value[i] >>= 3;
    }

    /* 滤波处理 */
    /* 首先从大到小排序 */
    for (i = 0; i < (TOUCH_READ_TIMES - 1); i++)
    {
        for (j = i + 1; j < TOUCH_READ_TIMES; j++)
        {
            /* 采样值从大到小排序排序 */
            if(read_value[i] < read_value[j])
            {
                value = read_value[i];
				read_value[i] = read_value[j];
				read_value[j] = value;
            }   
        }       
    }
   
    /* 去掉最大值，去掉最小值，求平均值 */
    j = TOUCH_READ_TIMES - 1;
    total_value = 0;
    for (i = 1; i < j; i++)     //求y的全部值
    {
        total_value += read_value[i];
    }
    value = total_value / (TOUCH_READ_TIMES - 2);
      
    return value;
}
#else 
static uint16_t touch_read_data(uint8_t cmd)
{
    uint8_t send_buf[3] = {0};
    uint8_t recv_buf[3] = {0};
    uint16_t value = 0;

    send_buf[0] = cmd;
    send_buf[1] = 0xFF;
    send_buf[2] = 0xFF;

    rt_spi_transfer(spi_touch_device, send_buf, recv_buf, 3);

    value = recv_buf[1];
    value <<= 8;
    value |= recv_buf[2];
    value >>= 3;

    return value;
}
#endif

static uint8_t touch_read_xy(uint16_t *x_value, uint16_t *y_value)
{   
    uint16_t x_value1, y_value1, x_value2, y_value2;

    x_value1 = touch_read_data(TOUCH_X_CMD);
    y_value1 = touch_read_data(TOUCH_Y_CMD);
    x_value2 = touch_read_data(TOUCH_X_CMD);
    y_value2 = touch_read_data(TOUCH_Y_CMD);
    
    /* 查看两个点之间的只采样值差距 */
    if(x_value1 > x_value2)
    {
        *x_value = x_value1 - x_value2;
    }
    else
    {
        *x_value = x_value2 - x_value1;
    }

    if(y_value1 > y_value2)
    {
        *y_value = y_value1 - y_value2;
    }
    else
    {
        *y_value = y_value2 - y_value1;
    }
	
    /* 判断采样差值是否在可控范围内 */
	if((*x_value > TOUCH_MAX + 0) || (*y_value > TOUCH_MAX + 0))  
	{
		return 0xFF;
	}

    /* 求平均值 */
    *x_value = (x_value1 + x_value2) / 2;
    *y_value = (y_value1 + y_value2) / 2;

    /* 判断得到的值，是否在取值范围之内 */
    if((*x_value > TOUCH_X_MAX + 0) || (*x_value < TOUCH_X_MIN) || 
        (*y_value > TOUCH_Y_MAX + 0) || (*y_value < TOUCH_Y_MIN))
    {                   
        return 0xFF;
    }
 
    return 0; 
}

static uint8_t touch_read_adjust(uint16_t x, uint16_t y, uint16_t *x_value, uint16_t *y_value)
{
    uint8_t i;
    uint32_t time_count;

    /* 读取校正点的坐标 */
    lcd_clear(WHITE);
    lcd_draw_circle(x, y, 5, RED);
    rt_kprintf("please adjust touch <%d, %d>!\r\n", x, y);
    i = 0;
    while(1)
    {
        if(!touch_read_xy(x_value, y_value))
        {
            i++;
            if(i > 10)         //延时一下，以读取最佳值
            {
                lcd_draw_circle(x, y, 5, RED);
                return 0;
            }
               
        }
		time_count++;
        /* 超时退出 */
        if(time_count > 0xFFFFFFFE)
        {   
            lcd_draw_circle(x, y, 5, RED);
            return 0xFF;
        } 
        rt_thread_mdelay(100);
    }       
}

void touch_adjust(void)
{
    uint16_t px[2], py[2], x_pot[4], y_pot[4];
    float x_factor, y_factor;

    /* 读取第一个点 */
    if(touch_read_adjust(LCD_ADJX_MIN, LCD_ADJY_MIN, &x_pot[0], &y_pot[0]))
    {
        return;
    }   
    TOUCH_AdjDelay500ms();

    /* 读取第二个点 */
    if(touch_read_adjust(LCD_ADJX_MIN, LCD_ADJY_MAX, &x_pot[1], &y_pot[1]))
    {
        return;
    }   
    TOUCH_AdjDelay500ms();

    /* 读取第三个点 */
    if(touch_read_adjust(LCD_ADJX_MAX, LCD_ADJY_MIN, &x_pot[2], &y_pot[2]))
    {
        return;
    }   
    TOUCH_AdjDelay500ms();

    /* 读取第四个点 */
    if(touch_read_adjust(LCD_ADJX_MAX, LCD_ADJY_MAX, &x_pot[3], &y_pot[3]))
    {
        return;
    }   
    TOUCH_AdjDelay500ms();
    
    /* 处理读取到的四个点的数据，整合成对角的两个点 */
    px[0] = (x_pot[0] + x_pot[1]) / 2;
    py[0] = (y_pot[0] + y_pot[2]) / 2;
    px[1] = (x_pot[3] + x_pot[2]) / 2;
    py[1] = (y_pot[3] + y_pot[1]) / 2;

    /* 求出比例因数 */
    x_factor = (float)LCD_ADJ_X / (px[1] - px[0]);
    y_factor = (float)LCD_ADJ_Y / (py[1] - py[0]);  
    
    /* 求出偏移量 */
    touch_adjust_data.x_offset = (int16_t)LCD_ADJX_MAX - ((float)px[1] * x_factor);
    touch_adjust_data.y_offset = (int16_t)LCD_ADJY_MAX - ((float)py[1] * y_factor);

    /* 将比例因数进行数据处理，然后保存 */
    touch_adjust_data.x_factor = x_factor ;
    touch_adjust_data.y_factor = y_factor ;
    
    touch_adjust_data.posState = TOUCH_ADJ_OK;

    lcd_clear(WHITE);
    rt_kprintf("x_offset = %d\r\n", touch_adjust_data.x_offset);
    rt_kprintf("y_offset = %d\r\n", touch_adjust_data.y_offset);
    rt_kprintf("x_factor = %d\r\n", (int)(touch_adjust_data.x_factor * 100000));
    rt_kprintf("y_factor = %d\r\n", (int)(touch_adjust_data.y_factor * 100000));
    
    // static record adjust data          
}
MSH_CMD_EXPORT(touch_adjust, touch adjust for mcu lcd);

uint8_t touch_scan(void)
{
    if (touch_read_xy(&raw_touch.x, &raw_touch.y)) //没有触摸
    {
        return 0xFF;    
    }
    /* 根据物理坐标值，计算出彩屏坐标值 */
    raw_touch.lcdx = (uint16_t)((float)raw_touch.x * touch_adjust_data.x_factor + touch_adjust_data.x_offset);
    raw_touch.lcdy = (uint16_t)((float)raw_touch.y * touch_adjust_data.y_factor + touch_adjust_data.y_offset);
    
    /* 查看彩屏坐标值是否超过彩屏大小 */
    if(raw_touch.lcdx > _touch.info.range_x)
    {
        raw_touch.lcdx = _touch.info.range_x;
    }
    if(raw_touch.lcdy > _touch.info.range_y)
    {
        raw_touch.lcdy = _touch.info.range_y;
    }
    return 0;     
}

////////////////////////////////////////////////////////////////////////////////////////////////
void spi1_electric_init(void)
{
    struct rt_spi_configuration config;

    config.data_width = 8;
    config.mode = RT_SPI_MASTER | RT_SPI_MODE_3 | RT_SPI_MSB;
    config.max_hz = 5 * 1000 * 1000;

    rt_spi_configure(spi_touch_device, &config);
}

static rt_size_t touch_readpoint(struct rt_touch_device *touch, void *buf, rt_size_t touch_num)
{
    RT_ASSERT(touch);
    RT_ASSERT(touch_num <= sizeof(struct rt_touch_data));

    struct rt_touch_data *pdata = (struct rt_touch_data *)buf;

    *pdata = touch_data;
    
    return 1;
}

static rt_err_t touch_control(struct rt_touch_device *touch, int cmd, void *arg)
{
    RT_ASSERT(touch);
    RT_ASSERT(arg);

    switch (cmd) {
    case RT_TOUCH_CTRL_GET_ID : 
        LOG_E("not support RT_TOUCH_CTRL_GET_ID");
        break;
    case RT_TOUCH_CTRL_GET_INFO : 
        rt_memcpy(arg, &_touch.info, sizeof(struct rt_touch_info));
        break;
    case RT_TOUCH_CTRL_SET_MODE :
        LOG_E("not support RT_TOUCH_CTRL_SET_MODE");
        break;
    case RT_TOUCH_CTRL_SET_X_RANGE : 
        LOG_E("not support RT_TOUCH_CTRL_SET_X_RANGE");
        break;
    case RT_TOUCH_CTRL_SET_Y_RANGE : 
        LOG_E("not support RT_TOUCH_CTRL_SET_Y_RANGE");
        break; 
    case RT_TOUCH_CTRL_SET_X_TO_Y  : 
        LOG_E("not support RT_TOUCH_CTRL_SET_X_TO_Y");
        break;
    case RT_TOUCH_CTRL_DISABLE_INT : 
        LOG_E("not support RT_TOUCH_CTRL_DISABLE_INT");
        break;
    case RT_TOUCH_CTRL_ENABLE_INT  : 
        LOG_E("not support RT_TOUCH_CTRL_ENABLE_INT");
        break;
    default :    
        LOG_E("not supported command %d", cmd); 
        break;
    }

    return RT_EOK;
}

static struct rt_touch_ops touch_ops = {
    .touch_readpoint = touch_readpoint,
    .touch_control = touch_control,
};

int drv_touch_hw_init(void)
{
    rt_err_t result = RT_EOK;
    struct rt_spi_device *spi_device;

    rt_memset(&_touch, 0, sizeof(_touch));

    rt_pin_mode(GET_PIN(D, 7), PIN_MODE_INPUT);

    rt_hw_spi_device_attach("spi1", "spi10", GPIOD, GPIO_PIN_6);
    spi_device = (struct rt_spi_device *)rt_device_find("spi10");
    if (spi_device == RT_NULL)
    {
        rt_kprintf("spi device %s not found!\r\n", "spi10");
        return -RT_ENOSYS;
    }
    spi_touch_device = spi_device;

    spi1_electric_init();

    _touch.info.type = RT_TOUCH_TYPE_RESISTANCE;
    _touch.info.vendor = RT_TOUCH_VENDOR_FT;
    _touch.info.point_num = 1;
    _touch.info.range_x = 240;
    _touch.info.range_y = 320;

    _touch.config.dev_name = "ILI9325";
    _touch.config.user_data = RT_NULL;

    _touch.ops = &touch_ops;
    
    result = rt_hw_touch_register(&_touch, "touch", RT_DEVICE_FLAG_RDWR, RT_NULL);
    if (result != RT_EOK)
    {
        LOG_E("tfttouch register err code: %d", result);
        return result;
    }
    LOG_E("tfttouch register success!\r\n");

    return result;
}
INIT_DEVICE_EXPORT(drv_touch_hw_init);

//////////////////////////////////////////////////////////////////////////////////////////////////
#define PEN_TOUCH_DOWN  0
#define PEN_TOUCH_UP    1

void touch_thread_entry(void *para)
{
    uint8_t prev_pen = PEN_TOUCH_UP;
    uint16_t pause_x = 0, pause_y = 0;

    LOG_I("touch scan..."); 
    while (1)
    {
        if (rt_pin_read(GET_PIN(D, 7)) == PEN_TOUCH_DOWN) // 有触摸
        {
            if (prev_pen == PEN_TOUCH_UP)
            {
                touch_data.event = RT_TOUCH_EVENT_DOWN;
            }
            else 
            {
                touch_data.event = RT_TOUCH_EVENT_NONE;
            }
            prev_pen = PEN_TOUCH_DOWN;

            if (touch_scan() == 0)
            {   
                // rt_kprintf("touch <%d, %d>\r\n", raw_touch.lcdx, raw_touch.lcdy);
                /* 触摸坐标换算 */
                if (tftlcd_data.dir == 0)
                {
                    touch_data.x_coordinate = raw_touch.lcdx;
                    touch_data.y_coordinate = raw_touch.lcdy;
                }
                else 
                {
                    touch_data.x_coordinate = raw_touch.lcdy;
                    touch_data.y_coordinate = _touch.info.range_x - raw_touch.lcdx;
                }

                /* 记录初始按下坐标 */
                if (touch_data.event == RT_TOUCH_EVENT_DOWN)
                {
                    pause_x = touch_data.x_coordinate;
                    pause_y = touch_data.y_coordinate;
                }
                if (((int)pause_x - (int)touch_data.x_coordinate) * ((int)pause_x - (int)touch_data.x_coordinate) + 
                    ((int)pause_y - (int)touch_data.y_coordinate) * ((int)pause_y - (int)touch_data.y_coordinate) > 10 * 10) // 半径10个像素点
                {
                    touch_data.event = RT_TOUCH_EVENT_MOVE;

                    pause_x = touch_data.x_coordinate;
                    pause_y = touch_data.y_coordinate;
                }

                touch_data.timestamp = rt_touch_get_ts();
                touch_data.width = 1;

                // lcd_fill(touch_data.x_coordinate - 1, touch_data.y_coordinate - 1, touch_data.x_coordinate + 2, touch_data.y_coordinate + 2, RED); 
            }
        }
        else 
        {
            if (prev_pen == PEN_TOUCH_DOWN)
            {
                touch_data.event = RT_TOUCH_EVENT_UP;
            }
            else 
            {
                touch_data.event = RT_TOUCH_EVENT_NONE;
            }
            prev_pen = PEN_TOUCH_UP;
        }

        if (touch_data.event != RT_TOUCH_EVENT_NONE)
        {
            // rt_kprintf("touch event %d\r\n", touch_data.event);

            #ifdef PKG_USING_LITTLEVGL2RTT
            littlevgl2rtt_send_input_event(touch_data.x_coordinate, touch_data.y_coordinate, touch_data.event);
            #endif
        }
        
        rt_thread_mdelay(5);  
    }
}

int create_touch_thread(void)
{
    rt_thread_t tid = RT_NULL;
    tid = rt_thread_create("touch", touch_thread_entry, RT_NULL, 1024, 23, 10);
    if (tid == RT_NULL)
    {
        return RT_ERROR;
    }
    rt_thread_startup(tid);

    return RT_EOK;
}
// INIT_DEVICE_EXPORT(create_touch_thread);

//////////////////////////////////////////////////////////////////////////////////////////////////
// #define BSP_USING_ONBOARD_TOUCH_TEST
#ifdef BSP_USING_ONBOARD_TOUCH_TEST
#include <stdlib.h> /* atoi */

void lcd_touch_scan(void *para)
{
    rt_kprintf("touch scan...\r\n"); 
    while (1)
    {
         
        #if 1
        if (rt_pin_read(GET_PIN(D, 7)) == 0) // 有触摸
        {
            if(touch_scan() == 0)
            {   
                rt_kprintf("touch <%d, %d>\r\n", raw_touch.lcdx, raw_touch.lcdy);
                // rt_kprintf("touch <%d, %d>\r\n", touch_read_data(TOUCH_X_CMD), touch_read_data(TOUCH_Y_CMD));   
                
                lcd_fill(raw_touch.lcdx - 1, raw_touch.lcdy - 1, 
                        raw_touch.lcdx + 2, raw_touch.lcdy + 2, RED);  
            }
        }
        #else 
        if(touch_scan() == 0)
        {   
            rt_kprintf("touch <%d, %d>\r\n", raw_touch.lcdx, raw_touch.lcdy);
  
            lcd_fill(raw_touch.lcdx - 1, raw_touch.lcdy - 1, 
                    raw_touch.lcdx + 2, raw_touch.lcdy + 2, RED);  
        }
        #endif 

        rt_thread_mdelay(20);  
    }
}

void lcd_touch(int argc, void **argv)
{
    static uint8_t touch_init = 0;
    rt_device_t touch = RT_NULL;
    rt_err_t ret;
    uint8_t send = 0;

    if(argc == 1)
    {
        rt_thread_t tid = RT_NULL;
        tid = rt_thread_create("t_touch", lcd_touch_scan, RT_NULL, 1024, 23, 10);
        rt_thread_startup(tid);
    }
    else if (argc >= 2)
    {
        if (rt_strcmp("x", argv[1]) == 0)
        {
            rt_kprintf("x = %d\r\n", touch_read_data(TOUCH_X_CMD));
        }
        else if (rt_strcmp("y", argv[1]) == 0)
        {
            rt_kprintf("y = %d\r\n", touch_read_data(TOUCH_Y_CMD));
        }
        else if (rt_strcmp("spi", argv[1]) == 0)
        {
            if (argc == 3)
            {
                send = (uint8_t)atoi(argv[2]);
            }
            
            rt_kprintf("spi = %d\r\n", spi1_read_write_byte(send));
        }
    }
}
MSH_CMD_EXPORT(lcd_touch, lcd touch test for mcu lcd);
#endif
